#ifndef __stack_sport_kenny_kfjiweur89u83r33e3e__
#define __stack_sport_kenny_kfjiweur89u83r33e3e__

#include <atomic>
#include <thread>
#include <functional>

#include <hude/utils/parses.hpp>
#include <hude/utils/serialport.hpp>

namespace hude
{
    namespace utils
    {
        /*********************************************************************
         * @brief 串口解析栈
         *          1. 构建一个串口解析栈首先需要提供ctx对象做事件源，可对裸数据的输入和输出事件设置回调
         *          2. open解析栈时要指定串口设备名和波特率，以及识别数据桢的扫描器
         *          3. output用于发送串口数据
         *          4. observe用于组织同步会话，指定要发送的数据桢以及一个回调用于监听和识别回应桢
         *          5. close用于结束串口解析栈
         */
        template< typename _ctx, typename _byte, size_t _buffers, size_t _frames >
        class stack_sport_t
        {
        public:
            using this_t     = stack_sport_t< _ctx, _byte, _buffers, _frames >;
            using ctx_t      = _ctx;
            using sport_t    = hude::utils::serialport_t;
            using parses_t   = hude::utils::parses_t< _byte, _buffers >;
            using buffer_t   = typename parses_t::buffer_t;
            using citr_t     = typename parses_t::citr_t;
            using on_raw_t   = typename parses_t::drop_t;
            using scaner_t   = typename parses_t::finder_t;
            using observe_t  = hude::utils::sync_observe_t< this_t, typename this_t::parses_t >;
            using state_t    = typename observe_t::result_t;
            using on_reply_t = typename observe_t::on_reply_t;

            enum : size_t { emFRAME_SIZE = _frames };

        public:
            /*********************************************************************
             * @brief 构建串口解析栈
             * @param ctx     ctx对象，提供可读事件源
             * @param drop    丢弃字节流事件
             * @param input   输入字节流事件
             * @param output  输出字节流事件
             */
            stack_sport_t( ctx_t ctx, on_raw_t drop = on_raw_t(), on_raw_t input = on_raw_t(), on_raw_t output = on_raw_t() ) :
                ctx_( ctx ), on_drop_( drop ), on_input_( input ), on_output_( output )
            {
            }

            /*********************************************************************
             * @brief 打开对象
             * @param sport      串口设备名
             * @param baudrate   波特率
             * @param scaner     数据桢识别器
             * @return 设备打开是否成功
             */
            bool open( const char* sport, int baudrate, scaner_t scaner );

            /*********************************************************************
             * @brief 关闭对象
             */
            void close();

            /*********************************************************************
             * @brief 输出字节流
             */
            bool output( const buffer_t& );
            inline bool operator()( const buffer_t& buffer ) { return output( buffer ); }

            /*********************************************************************
             * @brief 获得一个同步操作器
             * @return 返回的同步操作器
             */
            inline std::shared_ptr< observe_t > observe() { return observe_t{ this, &parses_ }; }

            /*********************************************************************
             * @brief 立即执行一次同步操作器
             * @param  buffer:    同步操作要发送的数据流
             * @param  on_reply:  同步操作接受的数据流处理，要判断接收的数据流哪些是被处理的，哪里不处理，以及是否结束本次同步操作的处理
             * @param  timeout:   设定同步操作的超时时间
             * @return 返回的同步执行的状态
             */
            inline state_t observe( const buffer_t& buffer, on_reply_t on_reply, size_t timeout );

            /*********************************************************************
             * @brief 获取基础解析栈对象的引用
             * @return 返回基础解析栈对象引用
             */
            inline parses_t& parses() { return parses_; }

        private:
            void on_readble( uint32_t events );

        private:
            std::atomic< bool >   running_;
            std::thread           thread_;
            ctx_t                 ctx_;
            sport_t               sport_;
            parses_t              parses_;
            on_raw_t              on_drop_;
            on_raw_t              on_input_;
            on_raw_t              on_output_;
        };

        /*********************************************************************
         * @brief 串口解析栈
         */
        template< typename _ctx, typename _byte, size_t _buffers, size_t _frames >
        bool stack_sport_t< _ctx, _byte, _buffers, _frames >::open( const char* dev, int speed, scaner_t scaner )
        {
            assert( ctx_ != nullptr );

            running_ = true;

            thread_ = std::thread( [this, scaner]
            {
                while( running_ )
                {
                    parses_.process( scaner, on_drop_ );
                }
            });

            if( ! sport_.open( dev, false ) ||
                ! sport_.settings( speed, 0U ) )
            {
                running_ = false;

                parses_.cannel();

                thread_.join();

                return false;
            }

            ctx_->evt_commit( sport_.handle(), std::bind( &this_t::on_readble, this, std::placeholders::_1 ) );

            return true;
        }

        /*********************************************************************
         * @brief 串口解析栈
         */
        template< typename _ctx, typename _byte, size_t _buffers, size_t _frames >
        void stack_sport_t< _ctx, _byte, _buffers, _frames >::close()
        {
            ctx_->evt_remove( sport_.handle() );

            running_ = false;

            parses_.cannel();

            thread_.join();

            sport_.close();
        }

        /*********************************************************************
         * @brief 串口解析栈
         */
        template< typename _ctx, typename _byte, size_t _buffers, size_t _frames >
        bool stack_sport_t< _ctx, _byte, _buffers, _frames >::output( const buffer_t& buffer )
        {
            if( on_output_ ) on_output_( buffer.begin(), buffer.end() );

            const size_t siz = buffer.size();
            auto ret = sport_.write( &(buffer[0]), siz );

            return ( ret < 0 )? false : siz == (size_t)ret;
        }

        /*********************************************************************
         * @brief 串口解析栈
         */
        template< typename _ctx, typename _byte, size_t _buffers, size_t _frames >
        inline typename stack_sport_t< _ctx, _byte, _buffers, _frames >::state_t
        stack_sport_t< _ctx, _byte, _buffers, _frames >::observe( const buffer_t& buffer, on_reply_t on_reply, size_t timeout )
        {
            observe_t obs{ this, &parses_ };

            return obs.request( buffer, on_reply, timeout );
        }

        /*********************************************************************
         * @brief 串口解析栈
         */
        template< typename _ctx, typename _byte, size_t _buffers, size_t _frames >
        void stack_sport_t< _ctx, _byte, _buffers, _frames >::on_readble( uint32_t events )
        {
            if( events & aio::emEPOLLIN )
            {
                auto item = parses_.buffer_alloc( emFRAME_SIZE );

                auto& buffer = *item;

                size_t count = 0;

                while( count < emFRAME_SIZE )
                {
                    auto ret = sport_.read( (char*)&(buffer[count]), emFRAME_SIZE - count );

                    if( ret <= 0 ) break;

                    count += ret;
                }

                buffer.resize( count );

                parses_.buffer_push( item );

                if( on_input_ ) on_input_( buffer.begin(), buffer.end() );
            }
        }

    } //namespace utils
} // namespace hude

#endif // __stack_sport_kenny_kfjiweur89u83r33e3e__

